﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.ADF;
//using ESRI.ArcGIS.ADF.Connection.Local;
using System.Reflection;
using PlanningGIS.Framework;

using System.Runtime.InteropServices;
using System.Threading;
using System.IO;
using System.Xml;
using PlanningGIS.ArcGIS;
using PlanningGIS.Util.Log;

namespace PlanningGIS.CoordCovert.model
{
   
    public class GeosocConvert2_2 : Operator2Ex
    {
        private const double UNINT = 0.00001;

        private IFeatureClass pFeatClsKzd;      //格网控制点要素类
        private List<IFeatureClass> pFeatClses;

        string fldX, fldY, fldDx, fldDy;        //格网控制点的X、Y、X偏移量、Y偏移量的属性字段名
        int idxFldX, idxFldY, idxFldDx, idxFldDy;//以上对应的字段索引
        double distance;//步长

        
        private object missing = Type.Missing;
        
        /// <summary>
        /// 需要进行转换的要素类集合
        /// </summary>
        public List<IFeatureClass> FeatClses
        {
            set { pFeatClses = value; }
        }

        /// <summary>
        /// 格网控制点要素类
        /// </summary>
        public IFeatureClass FeatClsKzd
        {
            set { pFeatClsKzd = value; }
        }

        /// <summary>
        /// 步长
        /// </summary>
        public Double Distance
        {
            set { distance = value; }
        }

        /// <summary>
        /// 格网控制点的X属性字段名
        /// </summary>
        public string FldX
        {
            set { fldX = value; }
        }

        /// <summary>
        /// 格网控制点的Y属性字段名
        /// </summary>
        public string FldY
        {
            set { fldY = value; }
        }

        /// <summary>
        /// 格网控制点的X偏移量属性字段名
        /// </summary>
        public string FldDx
        {
            set { fldDx = value; }
        }

        /// <summary>
        /// 格网控制点的Y偏移量属性字段名
        /// </summary>
        public string FldDy
        {
            set { fldDy = value; }
        }

        public GeosocConvert2_2()
        {
        }


        /// <summary>
        /// 数据转换
        /// </summary>
        /// <returns></returns>
        public override bool Operate()
        {
            if (pFeatClses == null || pFeatClses.Count == 0)
            {
               LogHelper.WriteErrLog("请设置需要进行变形的数据！");
                return false;
            }

            if (pFeatClsKzd == null)
            {
               LogHelper.WriteErrLog("请设置格网控制点要素类！");
                return false;
            }

            if (pFeatClsKzd.ShapeType != esriGeometryType.esriGeometryPoint)
            {
               LogHelper.WriteErrLog("格网控制点要素类必须是点层要素！");
                return false;
            }

            if (fldX == "" || fldY == "" || fldDx == "" || fldDy == "")
            {
               LogHelper.WriteErrLog("请设置正确的格网控制点要素类中的字段名称！");
                return false;
            }

            if (distance <= 0)
            {
               LogHelper.WriteErrLog("请设置正确的步长！");
                return false;
            }

            idxFldX = pFeatClsKzd.FindField(fldX);
            idxFldY = pFeatClsKzd.FindField(fldY);
            idxFldDx = pFeatClsKzd.FindField(fldDx);
            idxFldDy = pFeatClsKzd.FindField(fldDy);

            if (idxFldX == -1 || idxFldY == -1 || idxFldDx == -1 || idxFldDy == -1)
            {
               LogHelper.WriteErrLog("格网控制点要素类中不包含设置的字段，请检查！");
                return false;
            }

            setProcessBarMax.Invoke(pFeatClses.Count);

            int countTotal = 0;
            int errorCountTotal = 0;
            int rightCountTotal = 0;

            try
            {

                for (int i = 0; i < pFeatClses.Count; i++)
                {
                    if (stopAnyway)
                        return false;

                    setProcessBarCurrent.Invoke(i + 1);

                    IFeatureClass pFeatCls = pFeatClses[i];

                    int allCount = pFeatCls.FeatureCount(null);
                    if (allCount == 0) continue;

                    setSubProcessBarMax(allCount);

                    LogHelper.WriteLog("开始对数据[" + pFeatCls.AliasName + "]进行变形转换...");

                    #region 逐要素转换

                    IFeatureCursor pFeatCur = pFeatCls.Update(null, false);
                    IFeature pFeature = pFeatCur.NextFeature();

                    int count = 0;
                    int rightCount = 0;
                    int errorCount = 0;

                    while (pFeature != null)
                    {
                        if (stopAnyway)
                            return false;

                        count++;
                        SetSubProcessBarCurrent.Invoke(count);


                        if (ConvertFeature(pFeature))
                        {
                            rightCount++;
                        }
                        else
                        {
                            errorCount++;
                        }
                        pFeatCur.UpdateFeature(pFeature);
                        pFeature = pFeatCur.NextFeature();
                    }

                    if (errorCount == 0)
                    {
                        LogHelper.WriteLog("[" + pFeatCls.AliasName + "]数据转换完毕。待转换总数：" + count + "；成功：" + rightCount + "；失败：" + errorCount + "\n", zjgisLogType.zjgisLogDone);
                    }
                    else
                    {
                        LogHelper.WriteLog("[" + pFeatCls.AliasName + "]数据转换完毕。待转换总数：" + count + "；成功：" + rightCount + "；失败：" + errorCount + "\n", zjgisLogType.zjgisLogError);
                    }

                    countTotal += count;
                    rightCountTotal += rightCount;
                    errorCountTotal += errorCount;

                    Marshal.ReleaseComObject(pFeatCur);

                    #endregion

                    //AddFeatureclassSpatialIndex(pFeatCls, 0, 0, 0);
                }

                if (errorCountTotal == 0)
                {
                    LogHelper.WriteDoneLog("所有数据转换完毕。待转换总数：" + countTotal + "；成功：" + rightCountTotal + "；失败：" + errorCountTotal + "\n");
                }
                else
                {
                   LogHelper.WriteErrLog("所有数据转换完毕。待转换总数：" + countTotal + "；成功：" + rightCountTotal + "；失败：" + errorCountTotal + "\n");
                }

                return true;
            }
            catch (Exception ex)
            {
               LogHelper.WriteErrLog("转换失败：" + ex.Message);
                return false;
            }
        }

        #region private functions

        /// <summary>
        /// 对单个要素进行转换
        /// </summary>
        /// <param name="pFeature"></param>
        /// <returns>转换成功返回true、否则返回false</returns>
        private bool ConvertFeature(IFeature pFeature)
        {
            IGeometry pGeometry = pFeature.ShapeCopy;
            if (pGeometry.IsEmpty) return true;     //如果图形为空，则无需转换

            bool suc = true;

            try
            {
                if (pGeometry.GeometryType == esriGeometryType.esriGeometryMultipoint ||
                    pGeometry.GeometryType == esriGeometryType.esriGeometryPolyline)
                {
                    IPointCollection pPoints = pGeometry as IPointCollection;
                    for (int i = 0; i < pPoints.PointCount; i++)
                    {
                        IPoint pPoint = pPoints.get_Point(i);

                        suc = ConvertPoint(pPoint);
                        if (!suc) break;
                        pPoints.UpdatePoint(i, pPoint);
                    }

                    
                }
                //如果是面状数据，因为首尾点是同一个点，第一个点做了坐标转换后，最后一个点是不需要再进行转换的，否则该点（首/尾点）就会转换了两次
                else if (pGeometry.GeometryType == esriGeometryType.esriGeometryPolygon)
                {
                    #region 可以较好的转换面图层，但是Z值信息会丢失，转换后全为零
                    IGeometryCollection pGeometryNew = new GeometryBagClass();
                    IPolygon pPolygon = null;
                    ITopologicalOperator2 pTop = null;

                    IGeometryCollection pGeometries = pGeometry as IGeometryCollection;
                    if (pGeometries == null || pGeometries.GeometryCount == 0) return true;

                    bool hasZ = false;
                    IFeatureClass pFeatCls = (pFeature.Class as IFeatureClass);
                    IField pField = pFeatCls.Fields.get_Field(pFeatCls.FindField(pFeatCls.ShapeFieldName));
                    IFieldEdit pFieldEdit = pField as IFieldEdit;
                    IGeometryDef pGeometryDef = pFieldEdit.GeometryDef;
                    hasZ = pGeometryDef.HasZ;

                    for (int i = 0; i < pGeometries.GeometryCount; i++)
                    {
                        if (!suc) break;

                        IGeometry pGeomLow = pGeometries.get_Geometry(i);
                        IGeometry pGeomHigh = Convertor.ConvertLow2High(pGeomLow);

                        if (hasZ)
                        {
                            pGeomHigh = GeometryHelper.Geom2Dto3D(pGeomHigh);
                        }

                        IPointCollection pPoints = pGeomHigh as IPointCollection;

                        //最后一个点不用转！
                        for (int j = 0; j < pPoints.PointCount - 1; j++)
                        {
                            IPoint pPoint = pPoints.get_Point(j);

                            suc = ConvertPoint(pPoint);

                            if (!suc) break;

                            pPoints.UpdatePoint(j, pPoint);
                        }

                        if (pPolygon == null)
                        {
                            pPolygon = pGeomHigh as IPolygon;
                            pTop = pPolygon as ITopologicalOperator2;
                        }
                        else
                        {
                            pTop.IsKnownSimple_2 = false;
                            if (!pTop.IsKnownSimple) pTop.Simplify();

                            ITopologicalOperator2 pTopTemp = pGeomHigh as ITopologicalOperator2;
                            pTopTemp.IsKnownSimple_2 = false;
                            if (!pTopTemp.IsKnownSimple) pTopTemp.Simplify();

                            pPolygon = pTop.Union(pGeomHigh) as IPolygon;
                            pTop = pPolygon as ITopologicalOperator2;
                        }
                    }

                    pFeature.Shape = pPolygon;
                    #endregion
                }
                else if (pGeometry.GeometryType == esriGeometryType.esriGeometryPoint)
                {
                    IPoint pPoint = pGeometry as IPoint;

                    suc = ConvertPoint(pPoint);
                }
                else
                {
                   LogHelper.WriteErrLog("类型不支持！");
                    return false;
                }

                if (!suc)
                {
                   LogHelper.WriteErrLog("ID为[" + pFeature.OID.ToString() + "]的要素转超出了格网控制点的范围，无法进行变形！");
                    return false;

                }

                pFeature.Shape = pGeometry;
                //pFeature.Store();

                return true;
            }
            catch (Exception ex)
            {
               LogHelper.WriteErrLog("对ID为[" + pFeature.OID.ToString() + "]的要素转换失败：" + ex.Message);
                return false;
            }
        }

        private IFeature GetFeat(double x, double y,double dis)
        {
            
            //这里也要四舍五入，否则坑爹的VS还是会加一个或者减一个很小的数！SHIT!
            double tempx = ((int)(Math.Round(x / dis, 8))) * dis;
            double tempy = ((int)(Math.Round(y / dis, 8))) * dis;

            string sql = "";
            if (pFeatClsKzd.Fields.get_Field(idxFldX).Type == esriFieldType.esriFieldTypeDouble)
            {
                sql = fldX + " = " + tempx + " and " + fldY + " = " + tempy;
            }
            else if (pFeatClsKzd.Fields.get_Field(idxFldX).Type == esriFieldType.esriFieldTypeString)
            {
                sql = fldX + " = '" + tempx + "' and " + fldY + " = '" + tempy + "'";
            }

            IQueryFilter pQueryFilter = new QueryFilterClass();
            pQueryFilter.WhereClause = sql;
            IFeatureCursor pFeatCur = pFeatClsKzd.Search(pQueryFilter, false);
            IFeature pFeature = pFeatCur.NextFeature();
            Marshal.ReleaseComObject(pFeatCur);
            return pFeature;
        }

        /// <summary>
        /// 对某个点进行转换,纵方向取平均；横方向取加权
        /// </summary>
        /// <param name="pPoint"></param>
        private bool ConvertPoint(IPoint pPoint)
        {

            double x = pPoint.X;
            double y = pPoint.Y;


            double dis = distance;
            if (pPoint.SpatialReference is IGeographicCoordinateSystem)
            {
                dis = distance * UNINT;

                //先四舍五入到毫米级别
                x = Math.Round(x, 8);
                y = Math.Round(y, 8);
            }
            else
            {
                //同上
                x = Math.Round(x, 3);
                y = Math.Round(y, 3);
            }


            IFeature pFeatureLowLeft = GetFeat(x, y, dis);

            if (pFeatureLowLeft == null) return false;

            double x1 = Double.Parse(pFeatureLowLeft.get_Value(idxFldX).ToString());
            double y1 = Double.Parse(pFeatureLowLeft.get_Value(idxFldY).ToString());
            double dx1 = Double.Parse(pFeatureLowLeft.get_Value(idxFldDx).ToString());
            double dy1 = Double.Parse(pFeatureLowLeft.get_Value(idxFldDy).ToString());
            

            //保留这个误差，否则这些点会成为奇异点（大家都有误差，它没有，反而格格不入了！）
            ////如果该点就在格网控制点上（与控制点X、Y坐标之差小于1毫米），则直接取该控制点的偏移量 
            //if (Math.Abs(x1 - x) < (0.001 * dis / distance) && Math.Abs(y1 - y) < (0.001 * dis / distance))
            //{
            //    pPoint.X = x1 + dx1;
            //    pPoint.Y = y1 + dy1;

            //    return true;
            //}

            IFeature pFeatureUpLeft = GetFeat(x, y + dis, dis);
            if (pFeatureUpLeft == null) return false;

            dx1 = (dx1 + Double.Parse(pFeatureUpLeft.get_Value(idxFldDx).ToString())) / 2;
            dy1 = (dy1 + Double.Parse(pFeatureUpLeft.get_Value(idxFldDy).ToString())) / 2;

            IFeature pFeatureUpRight = GetFeat(x + dis, y + dis, dis);
            if (pFeatureUpRight == null) return false;

            IFeature pFeatureDownRight = GetFeat(x + dis, y, dis);
            if (pFeatureDownRight == null) return false;

            double x2 = Double.Parse(pFeatureUpRight.get_Value(idxFldX).ToString());
            double y2 = Double.Parse(pFeatureUpRight.get_Value(idxFldY).ToString());
            double dx2 = Double.Parse(pFeatureUpRight.get_Value(idxFldDx).ToString());
            double dy2 = Double.Parse(pFeatureUpRight.get_Value(idxFldDy).ToString());

            dx2 = (dx2 + Double.Parse(pFeatureDownRight.get_Value(idxFldDx).ToString())) / 2;
            dy2 = (dy2 + Double.Parse(pFeatureDownRight.get_Value(idxFldDy).ToString())) / 2;

            double X, Y;


            X = x + ((x - x1) * dx2 + (x2 - x) * dx1) / (x2 - x1);

            //Y = y + ((y - y1) * dy2 + (y2 - y) * dy1) / (y2 - y1);
            Y = y + ((x - x1) * dy2 + (x2 - x) * dy1) / (x2 - x1);

            pPoint.X = X;
            pPoint.Y = Y;

            return true;

        }

        /// <summary>
        /// 计算四参数
        /// </summary>
        /// <param name="x1"></param>
        /// <param name="y1"></param>
        /// <param name="x2"></param>
        /// <param name="y2"></param>
        /// <param name="X1"></param>
        /// <param name="Y1"></param>
        /// <param name="X2"></param>
        /// <param name="Y2"></param>
        /// <param name="dx"></param>
        /// <param name="dy"></param>
        /// <param name="alpa"></param>
        /// <param name="namta"></param>
        private void Compute(double x1, double y1, double x2, double y2, double X1, double Y1, double X2, double Y2, out double dx, out double dy, out double alpa, out double namta)
        {
            double TanAlpa = ((Y1 - Y2) * (x1 - x2) - (X1 - X2) * (y1 - y2)) / ((X1 - X2) * (x1 - x2) + (Y1 - Y1) * (y1 - y2));
            alpa = Math.Atan(TanAlpa);

            namta = (X1 - X2) / ((x1 - x2) * Math.Cos(alpa) - ((y1 - y2) * Math.Sin(alpa)));

            dx = X1 - namta * x1 * Math.Cos(alpa) + namta * y1 * Math.Sin(alpa);

            dy = Y1 - namta * x1 * Math.Sin(alpa) - namta * y1 * Math.Cos(alpa);
        }
        private double ComputeTanAlpa(double x1, double y1, double x2, double y2, double X1, double Y1, double X2, double Y2)
        {
            return ((Y1 - Y2) * (x1 - x2) - (X1 - X2) * (y1 - y2)) / ((X1 - X2) * (x1 - x2) + (Y1 - Y1) * (y1 - y2));
        }

        /// <summary>
        /// 删除要素类的空间索引
        /// </summary>
        /// <param name="pFeatureClass"></param>
        /// <returns></returns>
        private bool DeleteFeatureclassSpatialIndex(IFeatureClass pFeatureClass)
        {
            // Get an enumerator for indexes based on the shape field.
            IIndexes indexes = pFeatureClass.Indexes;
            string shapeFieldName = pFeatureClass.ShapeFieldName;
            IEnumIndex enumIndex = indexes.FindIndexesByFieldName(shapeFieldName);
            enumIndex.Reset();

            // Get the index based on the shape field (should only be one) and delete it.
            IIndex index = enumIndex.Next();

            if (index != null)
            {
                pFeatureClass.DeleteIndex(index);
            }

            return true;
        }

        /// <summary>
        /// 添加要素类的空间索引
        /// </summary>
        /// <param name="pFeatureClass"></param>
        /// <param name="pGridOneSize"></param>
        /// <param name="pGridTwoSize"></param>
        /// <param name="pGridThreeSize"></param>
        /// <returns></returns>
        private bool AddFeatureclassSpatialIndex(IFeatureClass pFeatureClass, Double gridOneSize, Double gridTwoSize, Double gridThreeSize)
        {
            // Clone the shape field from the feature class.
            string shapeFieldName = pFeatureClass.ShapeFieldName;
            int shapeFieldIndex = pFeatureClass.FindField(shapeFieldName);
            IFields fields = pFeatureClass.Fields;
            IField sourceField = fields.get_Field(shapeFieldIndex);
            IClone sourceFieldClone = (IClone)sourceField;
            IClone targetFieldClone = sourceFieldClone.Clone();
            IField targetField = (IField)targetFieldClone;

            // Open the geometry definition from the cloned field and modify it.
            IGeometryDef geometryDef = targetField.GeometryDef;
            IGeometryDefEdit geometryDefEdit = (IGeometryDefEdit)geometryDef;
            geometryDefEdit.GridCount_2 = 3;
            geometryDefEdit.set_GridSize(0, gridOneSize);
            geometryDefEdit.set_GridSize(1, gridTwoSize);
            geometryDefEdit.set_GridSize(2, gridThreeSize);

            // Create a spatial index and set the required attributes.
            IIndex newIndex = new IndexClass();
            IIndexEdit newIndexEdit = (IIndexEdit)newIndex;
            newIndexEdit.Name_2 = shapeFieldName + "_Index";
            newIndexEdit.IsAscending_2 = true;
            newIndexEdit.IsUnique_2 = false;

            // Create a fields collection and assign it to the new index.
            IFields newIndexFields = new FieldsClass();
            IFieldsEdit newIndexFieldsEdit = (IFieldsEdit)newIndexFields;
            newIndexFieldsEdit.AddField(targetField);
            newIndexEdit.Fields_2 = newIndexFields;

            // Add the spatial index back into the feature class.
            pFeatureClass.AddIndex(newIndex);

            return true;
        }

        #endregion
    }
}
